//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#ifndef STEMNETSTIMULUSBRAIN_H_
#define STEMNETSTIMULUSBRAIN_H_

#include "INETDefs.h"

//#include "AppBase.h"
#include "UDPBasicApp.h"
#include "KnowledgeRepository.h"
#include "StimulusMSG_m.h"
#include "GatewayMSG_m.h"
#include "ForwardableMSG_m.h"
#include "Emulate3G.h"

typedef enum {
    SINGLE,
    MULTIPLE
} ElectionType;

typedef enum {
    STEMNET,
    BEST_GW,
    PROBABILISTIC,
    DISTANCE
} ProtocolType;


typedef struct {

    IPvXAddress ip;
    double stim;
    simtime_t timestamp;

} Stimulus;

typedef struct {

    IPvXAddress ip;
    unsigned int last_id_sent;

} Sent_Stimulus;

typedef struct {

    unsigned int nHop;
    unsigned int last_id_seen;

} Forward_Map;

class INET_API StemNetStimulusBrain : public UDPBasicApp
{
public:
    StemNetStimulusBrain();
    virtual ~StemNetStimulusBrain();

    double calculateActualStimulus(void);
    double calculateActualThreshold(void);
    double calculateResponseFunction(double stim);

    // STATS
    double getAverageFinalRole(STEM_ROLE_t role, simtime_t time);

    bool getModule3GPresent(void) {return module3GPresent;}

protected:
  virtual int numInitStages() const {return 4;}
  virtual void initialize(int stage);
  virtual void handleMessageWhenUp(cMessage *msg);
  virtual void finish();

  virtual void handleSelfMsg(cMessage *msg);
  virtual void handleWifiMsg(cMessage *msg);

  virtual void processPacket(cPacket *msg);

  virtual double getResponseFuntion(void);

  //AppBase:
  //bool startApp(IDoneCallback *doneCallback);
  //bool stopApp(IDoneCallback *doneCallback);
  //bool crashApp(IDoneCallback *doneCallback);

private:
  void takeRoleDecision(void);
  void generateTraffic(void);
  void generateStimulus(void);
  void generateGateway(bool gwON, IPvXAddress addr, bool firstGW_ON);
  void generateGatewayON(IPvXAddress addr, bool first);
  void generateGatewayOFF(void);

  void checkStayGatewayOrLeave(void);

  bool isDuplicated(StimulusMSG * smsg);

  void broadcastPacket_1hop(cPacket *pkt);

  IPvXAddress chooseGatewayAddr(void);

  bool checkAlreaySeenAndForwardMessage(ForwardableMSG *forwardMsg);

  virtual void reactToStimulus(StimulusMSG *stimMsg);
  virtual void reactToGateway(GatewayMSG *gwMsg);
  virtual void reactToDeferTimeExpired(void);

  void reactToGateway_SingleElection(GatewayMSG *gwMsg);
  void reactToGateway_MultipleElection(GatewayMSG *gwMsg);

  void changeRole(STEM_ROLE_t newRole);

  void chooseAndElectTheBestGW(void);
  void chooseAndElectTheBestGW_old(void);
  void probabilisticBecomeGW(void);
  void distanceBecomeGW(void);

  void check3Gmodule(void);

  //stats
  void makePeriodocStats(void);
  void makeFinalStats(void);

protected:

    /** @name gate from Knowledge repository */
    /*@{*/
    int dbIn;
    /*@}*/

    /** @name gate to Knowledge repository */
    /*@{*/
    int dbOut;
    /*@}*/

    /** @name gate from wifi nic */
    /*@{*/
    int wifiIn;
    /*@}*/

    /** @name gate to wifi nic */
    /*@{*/
    int wifiOut;
    /*@}*/

    /** @name gate from dummy 3g module */
    /*@{*/
    int dummy3GIn;
    /*@}*/

    /** @name gate to dummy 3g module */
    /*@{*/
    int dummy3GOut;
    /*@}*/

    UDPSocket socketStimulus;
    int localPortStim, destPortStim;

private:
    int myApplicationAddress;
    IPvXAddress myIP;
    KnowledgeRepository *db;
    Emulate3G *module3g;

    cMessage *msgUpdateDBStep;

    cMessage *msgElectionDelay;
    IPvXAddress stimSrc;
    double CcellSrcStimulus;
    int newlyElectedGW;

    simtime_t decisionStepTime;
    cMessage *msgDecisionStep;

    simtime_t trafficStepTime;
    cMessage *msgTrafficStep;

    int maxStimulusStep;
    simtime_t stimulusBroadcastStepTime;
    cMessage *msgStimulusBroadcastStep;

    simtime_t gatewayBroadcastStepTime;
    cMessage *msgGatewayBroadcastStep;

    // election phase duration
    simtime_t timeElectionPhase;
    cMessage *endElectionPhase;

    cMessage *msgDebugStep;

    cMessage *msgStatisticalStep;

public:
    unsigned int forwardable_id;
private:
    simtime_t maxForwardDelay;

    cMessage *msgForwardDelay;

    int trafficMessageLength;

    // variable definitions for the status metric of a gateway
    double alpha;
    double beta;

    // variable definitions for the threshold metric
    double gamma;
    double delta;
    double eta;

    // functions activation
    bool sendStimulus;
    bool sendGatewayMsg;
    bool sendPeriodicGatewayMsg;

    // stimulus response function exponent
    int responseFunctionExponent;
    int totalResponseFunctionExponent;

    // number of new GW from a stimulus to another
    unsigned int numberOfNewGW;

    // probability to stay as gateway or leave the role
    double gatewayProbability;

    // probability to have got a 3G module
    double module3GProbability;
    bool module3GPresent;


    // election type
    ElectionType electionType;

    ProtocolType protocolType;
    double probabilisticValue;
    int distanceToGW;

    simtime_t protocolTimer;
    cMessage *msgProtocolTimer;

    bool becomeGWifAlone;
    bool stimulusOnRouting;

    // list to not send duplicate packets
    std::list<Sent_Stimulus> list_pkt_sent;

    cPacketQueue queueForwardMsg;

    std::map<IPvXAddress, Forward_Map> forwardMap;

    //stats
    cOutVector *byteGenerated;

    int countPktGenerated;
    int totalPktGenerated;
    cOutVector *pktGenerated;

    cOutVector *myActualRole;
    cOutVector *isGatewayRole;
    cOutVector *isTransitRole;
    cOutVector *isStubRole;
    std::list<std::pair<simtime_t, int> > gatewayList;
    std::list<std::pair<simtime_t, int> > transitList;
    std::list<std::pair<simtime_t, int> > stubList;

    cOutVector *actualEnergyVal;
};

#endif /* STEMNETSTIMULUSBRAIN_H_ */
